using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightToolbox
{
    
    public abstract class ControlExtenderBase : Control
    {
        public ControlExtenderBase()
        {
            this.Loaded += new EventHandler(ControlExtenderBase_Loaded);
        }

        void ControlExtenderBase_Loaded(object sender, EventArgs e)
        {
            FillInternalListsBasedOnNameProperties();
            RaiseBaseLoaded();
        }

        private void FillInternalListsBasedOnNameProperties()
        {
            IterateOverAllPropertiesAndSearchForPropertiesWithGetterSetterAndFillByNameAttrbiutes();
        }

        private void IterateOverAllPropertiesAndSearchForPropertiesWithGetterSetterAndFillByNameAttrbiutes()
        {
            foreach (PropertyInfo curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt in this.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic))
            {
                object[] FillByNameAttributes = curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt.GetCustomAttributes(typeof(FillXamlElementssByName), true);

                if (FillByNameAttributes.Length != 0)
                {
                    FillPropertyWithXamlElementsFromName(curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt, FillByNameAttributes);
                }
            }
        }

        private void FillPropertyWithXamlElementsFromName(PropertyInfo curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt, object[] FillByNameAttributes)
        {
            object currentPropertyValue = GetCurrentPropertyValue(curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt);
            foreach (FillXamlElementssByName fillXamlElementssByName in FillByNameAttributes)
            {
                Type GenericTypeForList = curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt.PropertyType.GetGenericArguments()[0];
                string propertyValueContainsCVSNames = GetPropertyNameValues(fillXamlElementssByName);
                if (propertyValueContainsCVSNames != null && !String.IsNullOrEmpty(propertyValueContainsCVSNames.ToString()))
                    foreach (string curElementNameToFindAndIfExistsToAdd in propertyValueContainsCVSNames.ToString().Replace(" ", "").Split(','))
                    {
                        CheckIfStringIsAValidFrameworkElementToBeAddedToThePropertyListBasedOnType(currentPropertyValue, GenericTypeForList, curElementNameToFindAndIfExistsToAdd);
                    }
            }
        }

        private void CheckIfStringIsAValidFrameworkElementToBeAddedToThePropertyListBasedOnType(object currentPropertyValue, Type GenericTypeForList, string curElementNameToFindAndIfExistsToAdd)
        {
            object curElementToFindAndIfExistsToAdd =
                this.FindName(curElementNameToFindAndIfExistsToAdd);
            if (curElementToFindAndIfExistsToAdd != null)
            {
                if (!GenericTypeForList.IsInstanceOfType(curElementToFindAndIfExistsToAdd))
                    throw new ArgumentException(curElementNameToFindAndIfExistsToAdd + " is not of type " + GenericTypeForList);
                AddToObjectWithAddMethod(currentPropertyValue, curElementToFindAndIfExistsToAdd);
            }
        }

        private object GetCurrentPropertyValue(PropertyInfo curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt)
        {
            object currentPropertyValue;
            try
            {
                currentPropertyValue =
                    curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt.GetValue(this, new object[] {});
            }
            catch(Exception ex)
            {
                throw new SilverlightToolboxException(string.Format("get or set for property {0}.{1} is not accessiable", curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt.ReflectedType.Name, curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt.Name), ex);
            }
            if (currentPropertyValue == null)
                throw new SilverlightToolboxException(string.Format("Please set an initial value for {0}", curPropertyInfoToCheckIfIsNameAndIfSoBuildListOnIt.Name));
            return currentPropertyValue;
        }

        private string GetPropertyNameValues(FillXamlElementssByName fillXamlElementssByName)
        {
            PropertyInfo propertyWithXamlElementsName =
                this.GetType().GetProperty(fillXamlElementssByName.NameOfPropertyWithNamesOfXAMLElements);
            object namesPropertyValue = propertyWithXamlElementsName.GetValue(this, new object[] { });
            if (namesPropertyValue != null)
                return namesPropertyValue.ToString();
            else
                return string.Empty;
        }


        public event EventHandler BaseLoaded;
        public void RaiseBaseLoaded()
        {
            if (BaseLoaded != null)
                BaseLoaded(this, new EventArgs());
        }



        private void AddToObjectWithAddMethod(object ObjectWithAddMethod, object itemToAdd)
        {
            MethodInfo addMethodInfo = ObjectWithAddMethod.GetType().GetMethod("Add");
            if (addMethodInfo != null)
            {
                addMethodInfo.Invoke(ObjectWithAddMethod, new object[] { itemToAdd });
            }
        }


        protected Canvas RootCanvas
        {
            get
            {
                return GetRootCanvas(this);
            }
        }

        public Canvas ParentCanvas
        {
            get
            {
                if (this.Parent == null)
                    return null;
                return this.Parent as Canvas;
            }
        }

        private static Canvas GetRootCanvas(FrameworkElement elementToCheckIfHasNoParentThenIsRoot)
        {
            if (elementToCheckIfHasNoParentThenIsRoot.Parent == null)
                return (Canvas)elementToCheckIfHasNoParentThenIsRoot;
            else
                return GetRootCanvas((FrameworkElement)elementToCheckIfHasNoParentThenIsRoot.Parent);
        }

        public bool Disabled
        {
            get;
            set;
        }

        protected bool Enabled
        {
            get
            {
                return !Disabled;
            }
            set
            {
                Disabled = !value;
            }
        }


        private new Point RenderTransformOrigin
        {
            get;
            set;
        }
    }
}